#include "remote/client/clientcontrolsession.h"
#include "remote/base/basedefines.h"
#include "remote/base/helps.h"

namespace remote {

ClientControlSession::ClientControlSession(boost::asio::io_service &io_service,
    const std::string base_name)
  : io_service_(io_service),
    session_name_(base_name) {
}

ClientControlSession::~ClientControlSession() {
}

bool ClientControlSession::Start(const char *server_addr, uint16 port) {
  control_connect_ = vzconn::VzClient::ConnectTcpServer(
                       io_service_,
                       server_addr,
                       port,
                       boost::bind(&ClientControlSession::OnConnectEvent,
                                   shared_from_this(), _1, _2));

  BOOST_ASSERT(control_connect_);
  control_connect_->SignalConnectError.connect(
    boost::bind(&ClientControlSession::OnConnectError,
                shared_from_this(), _1, _2));
  control_connect_->SignalConnectWrite.connect(
    boost::bind(&ClientControlSession::OnConnectWrite,
                shared_from_this(), _1));
  control_connect_->SignalConnectRead.connect(
    boost::bind(&ClientControlSession::OnConnectRead,
                shared_from_this(), _1, _2, _3, _4));
  return true;
}

void ClientControlSession::Stop() {
}

void ClientControlSession::OnConnectEvent(vzconn::VzConnect::Ptr connect,
    const boost::system::error_code& err) {
  BOOST_ASSERT(connect);
  BOOST_ASSERT(control_connect_ == connect);
  if(err) {
    LOG(ERROR) << "Failure to connect the server";
    SignalConnectFailure(shared_from_this(),
                         CLIENT_SESSION_ERROR_CODE_SERVER_UNKOWN);
    return;
  }
  StartRegisterClient();
}

void ClientControlSession::OnConnectError(vzconn::VzConnect::Ptr connect,
    const boost::system::error_code& err) {
  if(err) {
    LOG(ERROR) << err.message();
  }
  SignalConnectFailure(shared_from_this(),
                       CLIENT_SESSION_ERROR_CODE_SERVER_UNKOWN);
}

void ClientControlSession::OnConnectWrite(vzconn::VzConnect::Ptr connect) {
}

bool ClientControlSession::AsyncWrite(QueryStanza::Ptr stanza) {
  return control_connect_->AsyncWrite(stanza->ToByteBuffer());
}

void ClientControlSession::OnConnectRead(vzconn::VzConnect::Ptr connect,
    const char* buffer, int size, int flag) {
  // Handle Ping message
  if(flag == FLAG_PING_MESSAGE && size == 1) {
    connect->AsyncWrite(PING_MESSAGE, 1, FLAG_PING_MESSAGE);
    return;
  }
  QueryStanza::Ptr stanza(new QueryStanza);
  if(!stanza->Parse(buffer, size)) {
    LOG(ERROR) << "Failure to parse query stanza";
    return ;
  }
  if(flag == FLAG_REGISTER_MESSAGE) {
    ProcessRegisterMessage(stanza);
  }
  SignalSessionMessage(shared_from_this(), stanza);
}

void ClientControlSession::StartRegisterClient() {
  QueryStanza::Ptr register_stanza = QueryStanza::CreateQueryStanza(
                                       JSON_TYPE_REGISTER,
                                       STATE_REQUESTION_STATE,
                                       RandomString(DEFAULT_ID_SIZE),
                                       DEFAULT_SERVER_NAME,
                                       DEFAULT_CLIENT_NAME);
  Json::Value body;
  body[JSON_USERNAME] = session_name_;
  body[JSON_PASSWORD] = "TEST";
  register_stanza->set_body(body);
  control_connect_->AsyncWrite(register_stanza->ToByteBuffer(),
                               FLAG_REGISTER_MESSAGE);
}

void ClientControlSession::ProcessRegisterMessage(QueryStanza::Ptr stanza) {
  if(stanza->state() == STATE_CONTROL_REGISTER_SUCCEED) {
    server_name_ = stanza->from();
    session_name_ = stanza->to();
    SignalConnectSucceed(shared_from_this());
  } else {
    SignalConnectFailure(shared_from_this(),
                         CLIENT_SESSION_ERROR_CODE_REGISTER_ERROR);
  }
}

}
